In [1]:
from uniswap_fee_and_divergence import *
In [2]:
# Import pickle file
import pickle 

with open('backtest_results.pickle', 'rb') as handle:
    results = pickle.load(handle)
In [4]:
import plotly.graph_objects as go
import plotly.io as pio
pio.renderers.default='notebook'

def analyse_results(results):
    # Initialize empty DataFrame to hold summary stats
    summary_stats = pd.DataFrame()
    fig = go.Figure()

    # Loop through the results
    for result in results:
        # Get general info
        range_pct = result["range_pct"]
        is_hedged = result["is_hedged"]
        backtester = result["backtester"]

        # Get backtest DataFrame
        backtest_df = backtester.backtest_df

        # Calculate key stats
        fees_generated = backtest_df["fee"].sum()
        losses_due_to_divergence = backtest_df["divergence"].sum()
        pnl_total_with_fees = backtest_df.groupby("id")["pnl_total_with_fees"].last().sum()
        roi = backtester.roi
        max_dd = backtester.max_dd
        # Append stats to summary DataFrame
        summary_stats = pd.concat([summary_stats, pd.DataFrame({
            "Range Percentage": range_pct,
            "Is Hedged": is_hedged,
            "Number of positions": len(backtest_df.id.unique()),
            "Total Fees Generated": fees_generated,
            "Losses Due to Divergence": losses_due_to_divergence,
            "Total PNL with Fees": pnl_total_with_fees,
            "ROI": roi,
            "DD": max_dd,
            "ID": results.index(result)
        }, index=[0])])

        # Generate line for each backtest
        fig.add_trace(go.Scatter(
            x=backtest_df.index,
            y=backtest_df['net_usd_capital'],
            mode='lines',
            name=f'{range_pct}%, {"Hedged" if is_hedged else "Unhedged"}',
            #hovertemplate = '<b>Capital</b>: %{y:.2f}<extra></extra>'
        ))

    # Edit layout
    fig.update_layout(title='Backtest Capital Over Time for Different Configurations',
                      height=600,
                    xaxis_title='Time',
                    yaxis_title='Net USD Capital',
                    hovermode="x unified")
    
    fig.show()
    
    # Set index to the configurations for easier referencing
    summary_stats.set_index(["Range Percentage", "Is Hedged"], inplace=True)

    return summary_stats

summary_stats = analyse_results(results)

# Displaying the summary sorted by ROI
summary_stats.sort_values(by='ROI', ascending=False)
Out[4]:
Number of positions Total Fees Generated Losses Due to Divergence Total PNL with Fees ROI DD ID
Range Percentage Is Hedged
0.25 True 5427 3.045300e+06 -12.967646 367941.401915 157.673319 0.456666 24
False 5427 3.045300e+06 -19.306082 95305.687183 95.305687 0.526607 25
0.50 True 3349 1.522650e+06 -11.060791 175150.090926 80.303426 0.488346 22
0.75 True 2281 1.015100e+06 -10.327435 113217.506109 51.234421 0.540954 20
1.00 True 1658 7.613249e+05 -9.445027 89710.865240 41.840826 0.575305 18
0.50 False 3349 1.522650e+06 -16.769627 30933.574210 30.933574 0.608637 23
2.00 True 694 3.806624e+05 -4.406666 45732.541752 14.829102 0.625625 16
0.75 False 2281 1.015100e+06 -15.555946 7401.576860 7.401577 0.677577 21
3.00 True 397 2.537750e+05 -9.343309 28719.668960 0.909471 0.634923 14
1.00 False 1658 7.613249e+05 -14.536074 678.959142 0.678959 0.700314 19
4.00 True 270 1.903312e+05 -15.913529 20886.601208 -4.408404 0.658010 12
6.00 True 131 1.268875e+05 -24.717941 15276.492192 -8.258188 0.635138 8
5.00 True 187 1.522650e+05 -14.110463 14447.637649 -10.557003 0.634995 10
7.00 True 105 1.087607e+05 -32.574201 8627.287566 -15.938392 0.636237 6
9.00 True 66 8.459165e+04 14.890108 9694.161175 -16.312923 0.580023 2
2.00 False 694 3.806624e+05 -10.881516 -18693.215035 -18.693215 0.711620 17
8.00 True 92 9.516561e+04 -26.127067 5298.602725 -20.605073 0.623922 4
10.00 True 63 7.613249e+04 -24.565869 1048.318563 -24.396788 0.604139 0
3.00 False 397 2.537750e+05 -15.821403 -28648.366485 -28.648366 0.699935 15
4.00 False 270 1.903312e+05 -22.023441 -30425.783004 -30.425783 0.724144 13
6.00 False 131 1.268875e+05 -32.427186 -31702.711884 -31.702712 0.677762 9
5.00 False 187 1.522650e+05 -22.444481 -35212.912365 -35.212912 0.676395 11
9.00 False 66 8.459165e+04 -7.029798 -36165.767882 -36.165768 0.605812 3
7.00 False 105 1.087607e+05 -39.596866 -37328.381575 -37.328382 0.661893 7
8.00 False 92 9.516561e+04 -38.529036 -40540.446266 -40.540446 0.654174 5
10.00 False 63 7.613249e+04 -36.500826 -43285.531112 -43.285531 0.603830 1

Uniswap V3 Strategy Backtest Results¶

Executive Summary¶

This internal document presents the result of our backtest on a Uniswap V3 strategy for the ETH/USDC pool with the aim to assess the impact of different configurations on performance, specifically focusing on the range and whether a hedging strategy was implemented.

Our findings indicate that both range percentage and hedging are vital factors influencing the strategy's performance. The key results of our backtest are summarized below:

  • The configuration that achieved the highest ROI (157.67%) had a range percentage of 0.25 and utilized hedging. This configuration also had the lowest drawdown (45.7%), indicating a relatively lower risk compared to other configurations.

  • Overall, configurations with hedging resulted in a better ROI than those without. For example, when a range of 0.25% was used, the hedged strategy achieved a significantly higher ROI (157.67%) compared to the unhedged strategy (95.31%). This suggests that hedging could effectively offset some of the losses due to the divergence, leading to better overall performance.

  • Our analysis also shows that a larger range doesn't necessarily equate to better performance. The configuration with the highest range (10%) had one of the lowest ROIs, whether hedged (-24.40%) or unhedged (-43.29%). This could be due to the fact that larger ranges may result in lower concentrated liquidity multipler, thus generating fewer fees.

  • Conversely, a smaller range, specifically 0.25%, achieved the best results in both hedged and unhedged scenarios. This suggests that a more frequent trading approach could be advantageous, given the fee structure of Uniswap V3.

  • As expected, hedging helped decrease the directional risk. For each range, hedged strategies consistently exhibited a lower drawdown than their unhedged counterparts. This clearly indicates the risk-mitigation benefit of hedging.

Based on these findings, we suggest further exploration of the following additional strategies to increase return and decrease directional risks:

  1. Adjusting the range more dynamically: Instead of sticking to a single static range for the entire duration, the strategy could adjust its range based on market conditions. For example, it could use a larger range in more volatile conditions and a smaller one in less volatile conditions.

  2. Incorporating predictive models: Machine learning models could be utilized to predict the future price of ETH and adjust the range or the hedging strategy accordingly.

  3. Implementing stop-loss rules: To further control risk, the strategy could close the position and hedge it once the loss reaches a certain threshold.

We hope these insights are helpful in refining our Uniswap V3 strategy. We recommend conducting additional backtests incorporating the strategies suggested above for further optimization.

Latest version of the nalysis notebook can be found here:¶

  • HTML
  • IPynb